import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { i18n } from '@app-nextmesh/common/utils';
import { useModelStore } from '@app-nextmesh/store/model';
import {
  ENTITY_TARGET,
  ENTITY_TYPE_ALL,
  ENTITY_TYPES,
  ModelEventType,
  ModelPickEvent,
  ModelPickSwitchEvent,
  ModelTheme,
} from '@app-nextmesh/types';
import { events } from '@app-nextmesh/utils';
import { isArray } from 'lodash';
import { useCallback, useEffect, useRef, useState } from 'react';
import { ErrorTips, FormFieldSet } from '.';
import { IBaseProps } from './types';

interface IProps extends IBaseProps<string[]> {
  disabled: boolean;
  switchedOn?: boolean;
  name: string; // Selection、Source、Target 等
  type: string | string[]; // Vertex 等
  label?: string;
  assignedValues: Set<string>;
  onClick: (name: string) => void;
  visible?: boolean;
}

function getInvalidTypes(type: string | string[]) {
  if (isArray(type)) {
    return type.filter((t) => !ENTITY_TYPES.includes(t));
  } else {
    return !ENTITY_TYPES.includes(type) ? [type] : [];
  }
}

/**
 * entity(点、边、面、体)切换类型、列表显示 组件
 */
export default function Entity({
  disabled,
  switchedOn,
  name: entityName,
  type: entityType,
  value: initIndices,
  assignedValues,
  onChange: onInputChange,
  onClick,
  visible = true,
}: IProps) {
  const [indices, setIndices] = useState<string[]>(null);
  // 模型是否加载完成
  const modelLoaded = useModelStore((state) => state.loaded);
  // 其他同组条件使用的entity values
  const otherValues = useRef<string[]>([]);
  const entityClass = ENTITY_TARGET.includes(entityName) ? 'TARGET' : 'SOURCE';
  const modelTheme = ENTITY_TARGET.includes(entityName)
    ? ModelTheme.Target
    : ModelTheme.Source;

  const pickListener = useCallback(
    (event: ModelPickEvent) => {
      if (!switchedOn) {
        return;
      }
      let newIndices = [];
      const selected: string[] = [];
      if (event.selected) {
        // 全量更新，直接赋值
        newIndices = event.indices;
        otherValues.current.forEach((value) => {
          const index = newIndices.indexOf(value);
          if (index >= 0) {
            selected.push(...newIndices.splice(index, 1));
          }
        });
      } else {
        // 取消选中，逐个删除
        newIndices = indices ?? [];
        for (const index of event.indices) {
          const i = newIndices.indexOf(index);
          if (i >= 0) {
            newIndices.splice(i, 1);
          }
        }
      }
      if (selected.length > 0) {
        MessageTip.error(` ${selected.join(',')} 已被其他条件使用`);
        events.emit(ModelEventType.SetPicked, {
          indices: newIndices,
          theme: modelTheme,
        });
        return;
      }
      setIndices(newIndices);
      onInputChange(newIndices);
    },
    [otherValues.current, switchedOn, entityName, modelTheme],
  );

  function switchListener(event: ModelPickSwitchEvent) {
    if (event.clear) {
      const newIndices = [];
      setIndices(newIndices);
      onInputChange(newIndices);
    }
  }

  useEffect(() => {
    setIndices(initIndices.filter((id) => id != null));
  }, [initIndices]);

  useEffect(() => {
    if (indices == null) {
      return;
    }
    // 更新其他同组条件使用的entity values
    otherValues.current = Array.from(assignedValues).filter(
      (value) => !indices.includes(value),
    );
  }, [assignedValues, indices]);

  useEffect(() => {
    // 模型加载完成，且切换到当前entity时，订阅事件
    if (!modelLoaded) {
      return () => {};
    }
    if (switchedOn && !disabled) {
      events.on(ModelEventType.Pick, pickListener);
    } else {
      events.off(ModelEventType.Pick, pickListener);
    }
    return () => {
      events.off(ModelEventType.Pick, pickListener);
    };
  }, [modelLoaded, switchedOn, disabled]);

  useEffect(() => {
    if (modelLoaded) {
      // 类型和值发生变化时，触发监听器
      events.emit(ModelEventType.SetPicked, {
        indices,
        theme: modelTheme,
      });
    }
  }, [modelLoaded, indices]);

  useEffect(() => {
    if (switchedOn) {
      events.emit(ModelEventType.SwitchTheme, modelTheme);
    }
  }, [switchedOn, modelTheme]);
  useEffect(() => {
    events.on(ModelEventType.SetCurrentPickType, switchListener);
    return () => {
      events.off(ModelEventType.SetCurrentPickType, switchListener);
    };
  }, []);
  useEffect(() => {
    return () => {
      // 清除选中状态等
      events.emit(ModelEventType.SetPicked, {
        indices: [],
        theme: modelTheme,
      });
      events.emit(ModelEventType.PickTypes, { types: ENTITY_TYPES });
      events.emit(ModelEventType.SwitchTheme, ModelTheme.Default);
      // 重置为智能(ALL)拾取
      events.emit(ModelEventType.SetCurrentPickType, {
        type: ENTITY_TYPE_ALL,
        clear: true,
      });
    };
  }, []);
  const updateEntityList = (newIndices: string[]) => {
    setIndices(newIndices);
    onInputChange(newIndices);
    events.emit(ModelEventType.SetPicked, {
      indices: newIndices,
      theme: modelTheme,
    });
  };
  const handleEntityItemRemove = (value: string) => {
    const newIndices = [...indices];
    const index = newIndices.indexOf(value);
    if (index >= 0) {
      newIndices.splice(index, 1);
      updateEntityList(newIndices);
    }
  };
  const handleEntityListClear = () => {
    updateEntityList([]);
  };
  if (!indices) {
    return <></>;
  }
  const disabled2 = !modelLoaded || disabled;
  return getInvalidTypes(entityType).length > 0 ? (
    <ErrorTips>
      {`${entityName}包含无效的Type类型: ${getInvalidTypes(entityType).join(', ')}`}
    </ErrorTips>
  ) : (
    <>
      {visible && (
        <FormFieldSet>
          <div
            onClick={(e) => {
              onClick(entityName);
              e.stopPropagation();
            }}
            className={`nm-entityAssignment  ${disabled2 ? 'nm-entityAssignment--disabled' : switchedOn ? 'nm-entityAssignment--switchedOn' : ''}`}
          >
            <EntityFormHeader
              length={indices.length}
              onClear={handleEntityListClear}
              disabled={disabled2}
            />
            <EntityFormList
              list={indices}
              entityClass={entityClass}
              disabled={disabled}
              onRemove={handleEntityItemRemove}
            />
          </div>
        </FormFieldSet>
      )}
    </>
  );
}

function EntityFormHeader({
  length,
  onClear,
  disabled,
}: {
  length: number;
  onClear: () => void;
  disabled: boolean;
}) {
  const disabled2 = length === 0 || disabled;
  return (
    <div className='nm-entityAssignment__header'>
      <label className='nm-entityAssignment__headerText'>
        <span>{i18n.t('entity')}</span>
        <span className='nm-entityAssignment__headerSubtitle'>{`(${length})`}</span>
      </label>
      <button
        disabled={disabled2}
        onClick={(e) => {
          onClear();
          e.stopPropagation();
        }}
        className={`nm-entityAssignment__clear ${disabled2 ? 'nm-entityAssignment__clear--disabled' : ''}`}
      >
        <span>{i18n.t('clearList')}</span>
      </button>
    </div>
  );
}

function EntityFormList({
  list,
  entityClass,
  disabled,
  onRemove,
}: {
  list: string[];
  entityClass: string;
  disabled: boolean;
  onRemove: (value: string) => void;
}) {
  const makeItemLabel = (value: string) => {
    let label = value;
    const lastIndex = label.lastIndexOf('-');
    if (lastIndex >= 0) {
      label = label.substring(lastIndex + 1);
    }
    return label;
  };
  const children =
    list.length === 0 ? (
      <span className='nm-entityAssignment__noSelections'>
        {i18n.t('noEntityAssigned')}
      </span>
    ) : (
      list.map((value) => (
        <div
          key={value}
          className={`nm-entityAssignment__item nm-entityAssignment__item--${entityClass}_ASSIGNMENT`}
        >
          <div className={`nm-entityAssignment__itemName`}>
            {makeItemLabel(value)}
          </div>
          {!disabled && (
            <button
              onClick={() => onRemove(value)}
              className='nm-entityAssignment__itemRemoveButton'
            />
          )}
        </div>
      ))
    );
  return (
    <div
      className={`nm-entityAssignment__list nm-entityAssignment__list--${entityClass}_ASSIGNMENT`}
    >
      {children}
    </div>
  );
}
